//---------------------------------------------------------------------------
//
// Name:        RSSFilterFrame.cpp
// Author:      gurkesaft
// Created:     7/29/2007 10:33:08 PM
// Description: RSSFilterFrame class implementation
//
//---------------------------------------------------------------------------

#include "RSSFilterFrame.h"
#include "wx/textdlg.h"

//Do not add custom headers between
//Header Include Start and Header Include End
//wxDev-C++ designer will remove them
////Header Include Start
////Header Include End

//----------------------------------------------------------------------------
// RSSFilterFrame
//----------------------------------------------------------------------------
//Add Custom Events only in the appropriate block.
//Code added in other places will be removed by wxDev-C++
////Event Table Start
BEGIN_EVENT_TABLE(RSSFilterFrame,wxFrame)
	////Manual Code Start
	////Manual Code End

	EVT_CLOSE(RSSFilterFrame::OnClose)
	EVT_TIMER(ID_TIMERFETCHSINGLE,RSSFilterFrame::timerFetchSingleTimer)
	EVT_TIMER(ID_TIMERFETCH,RSSFilterFrame::timerFetchTimer)
	EVT_BUTTON(ID_BUTTONFETCH,RSSFilterFrame::buttonFetchClick)
	EVT_BUTTON(ID_BUTTONSHOW,RSSFilterFrame::buttonShowClick)
	EVT_BUTTON(ID_BUTTONRECALL,RSSFilterFrame::buttonRecallClick)
	EVT_BUTTON(ID_BUTTONNEWFEED,RSSFilterFrame::buttonNewFeedClick)
	EVT_BUTTON(ID_BUTTONREMOVEFEED,RSSFilterFrame::buttonRemoveFeedClick)
	EVT_CHECKBOX(ID_CHECKBOXLIMIT,RSSFilterFrame::checkBoxLimitClick)
	EVT_BUTTON(ID_BUTTONQUIT,RSSFilterFrame::buttonQuitClick)
	EVT_BUTTON(ID_BUTTONUBERFETCH,RSSFilterFrame::buttonUberFetchClick)
	EVT_TEXT_ENTER(ID_EDITREJECT,RSSFilterFrame::editRejectEnter)
	EVT_CHECKBOX(ID_CHECKBOXCREATOR,RSSFilterFrame::checkBoxTitleClick)
	EVT_CHECKBOX(ID_CHECKBOXDESCRIPTION,RSSFilterFrame::checkBoxTitleClick)
	EVT_CHECKBOX(ID_CHECKBOXTITLE,RSSFilterFrame::checkBoxTitleClick)
	EVT_TEXT_ENTER(ID_EDITFILTER,RSSFilterFrame::editFilterEnter)

	EVT_UPDATE_UI(ID_PANELMAIN,RSSFilterFrame::panelMainUpdateUI)
END_EVENT_TABLE()
////Event Table End

wxString JaxInputStream::GetEverything()
{
    char buffer[1024];
    wxString everything=wxT("");

    do
    {
        Read(buffer, 1024);
        everything.Append((wxChar*)buffer, LastRead());
    } while(LastRead() > 0 );

    return everything;
}




RSSFilterFrame::RSSFilterFrame(wxWindow *parent, wxWindowID id, const wxString &title, const wxPoint &position, const wxSize& size, long style)
: wxFrame(parent, id, title, position, size, style)
{
	CreateGUIControls();
}

RSSFilterFrame::~RSSFilterFrame()
{
}

void RSSFilterFrame::CreateGUIControls()
{

    // Set up the default font.
    SetFont(wxFont(8, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));



	//Do not add custom code between
	//GUI Items Creation Start and GUI Items Creation End
	//wxDev-C++ designer will remove them.
	//Add the custom code before or after the blocks
	////GUI Items Creation Start

	panelMain = new wxPanel(this, ID_PANELMAIN, wxPoint(0, 0), wxSize(500, 500));
	panelMain->SetBackgroundColour(wxColour(239,241,250));


	// The list on the side.
	wxArrayString arrayStringFor_listURLs;
	listURLs = new wxListBox(panelMain, ID_LISTURLS, wxPoint(308, 140), wxSize(88, 167), arrayStringFor_listURLs, wxLB_SINGLE);
	listURLs->Append(wxT("prl"));
	listURLs->Append(wxT("cond-mat"));

    checkBoxLimit = new wxCheckBox(panelMain, ID_CHECKBOXLIMIT, wxT("Limit:"), wxPoint(210, 3), wxDefaultSize, 0, wxDefaultValidator, wxT("checkBoxLimit"));


	checkBoxTitle = new wxCheckBox(panelMain, ID_CHECKBOXTITLE, wxT("Title"), wxPoint(50, 74), wxSize(70, 17), 0, wxDefaultValidator, wxT("checkBoxTitle"));
	checkBoxTitle->SetValue(true);

	checkBoxDescription = new wxCheckBox(panelMain, ID_CHECKBOXDESCRIPTION, wxT("Description"), wxPoint(110, 74), wxSize(120, 17), 0, wxDefaultValidator, wxT("checkBoxDescription"));
	checkBoxDescription->SetValue(true);

	checkBoxCreator = new wxCheckBox(panelMain, ID_CHECKBOXCREATOR, wxT("Creator"), wxPoint(210, 74), wxSize(100, 17), 0, wxDefaultValidator, wxT("checkBoxCreator"));
	checkBoxCreator->SetValue(true);

    checkBoxDebug     = new wxCheckBox(panelMain, ID_CHECKBOXDEBUG,     wxT("Debug"),     wxPoint(50, 120),  wxSize(70, 17), 0, wxDefaultValidator, wxT("checkBoxDebug"));
    checkBoxUberdebug = new wxCheckBox(panelMain, ID_CHECKBOXUBERDEBUG, wxT("Uberdebug"), wxPoint(125, 120), wxSize(100, 17), 0, wxDefaultValidator, wxT("checkBoxUberdebug"));


	WxStaticText1 = new wxStaticText(panelMain, ID_WXSTATICTEXT1, wxT("Search"), wxPoint(4, 31), wxDefaultSize, 0, wxT("WxStaticText1"));
    editFilter = new wxTextCtrl(panelMain, ID_EDITFILTER, wxT("quantum|magnet+spin"), wxPoint(50, 29), wxSize(348, 19), wxTE_PROCESS_ENTER, wxDefaultValidator, wxT("editFilter"));

	WxStaticText2 = new wxStaticText(panelMain, ID_WXSTATICTEXT2, wxT("Reject"), wxPoint(4, 53), wxDefaultSize, 0, wxT("WxStaticText2"));
    editReject    = new wxTextCtrl(panelMain, ID_EDITREJECT, wxT("updated"), wxPoint(50, 51), wxSize(348, 19), wxTE_PROCESS_ENTER, wxDefaultValidator, wxT("editReject"));

	WxStaticText3 = new wxStaticText(panelMain, ID_WXSTATICTEXT3, wxT("Include"), wxPoint(4, 75), wxDefaultSize, 0, wxT("WxStaticText3"));

	WxStaticText4 = new wxStaticText(panelMain, ID_WXSTATICTEXT4, wxT("Email"), wxPoint(4, 97), wxDefaultSize, 0, wxT("WxStaticText4"));
    editEmail = new wxTextCtrl(panelMain, ID_EDITEMAIL, wxT("your@email.here"), wxPoint(50, 94), wxSize(348, 19), 0, wxDefaultValidator, wxT("editEmail"));


	memoLog = new wxTextCtrl(panelMain, ID_MEMOLOG, wxEmptyString, wxPoint(4, 140), wxSize(301, 227), wxTE_MULTILINE, wxDefaultValidator, wxT("memoLog"));
	memoLog->SetMaxLength(0);
	memoLog->SetFocus();
	memoLog->SetInsertionPointEnd();



	WxStaticText5 = new wxStaticText(panelMain, ID_WXSTATICTEXT5, wxT("Log"), wxPoint(4, 122), wxDefaultSize, 0, wxT("WxStaticText5"));
	WxStaticText5->SetFont(wxFont(8, wxSWISS, wxNORMAL, wxBOLD, false));

	WxStaticText6 = new wxStaticText(panelMain, ID_WXSTATICTEXT6, wxT("Feeds"), wxPoint(308, 122), wxDefaultSize, 0, wxT("WxStaticText6"));
	WxStaticText6->SetFont(wxFont(8, wxSWISS, wxNORMAL, wxBOLD, false));

	WxStaticText7 = new wxStaticText(panelMain, ID_WXSTATICTEXT7, wxT("Filter Configuration"), wxPoint(4, 7), wxDefaultSize, 0, wxT("WxStaticText7"));
	WxStaticText7->SetFont(wxFont(8, wxSWISS, wxNORMAL, wxBOLD, false));


	spinLimit = new wxSpinCtrl(panelMain, ID_SPINLIMIT, wxT("10000"), wxPoint(272, 4), wxSize(79, 22), wxSP_ARROW_KEYS, 100, 1000000, 10000);

	WxStaticText8 = new wxStaticText(panelMain, ID_WXSTATICTEXT8, wxT("Articles"), wxPoint(354, 8), wxDefaultSize, 0, wxT("WxStaticText8"));


	buttonRemoveFeed = new wxButton(panelMain, ID_BUTTONREMOVEFEED, wxT("Remove"), wxPoint(342, 310), wxSize(55, 21), 0, wxDefaultValidator, wxT("buttonRemoveFeed"));

	buttonNewFeed = new wxButton(panelMain, ID_BUTTONNEWFEED, wxT("Add"), wxPoint(308, 310), wxSize(33, 21), 0, wxDefaultValidator, wxT("buttonNewFeed"));

	buttonRecall = new wxButton(this, ID_BUTTONRECALL, wxT("Recall"), wxPoint(236, 276), wxSize(45, 21), 0, wxDefaultValidator, wxT("buttonRecall"));
	buttonRecall->Show(false);

	buttonShow = new wxButton(this, ID_BUTTONSHOW, wxT("Show"), wxPoint(174, 276), wxSize(49, 21), 0, wxDefaultValidator, wxT("buttonShow"));
	buttonShow->Show(false);

	buttonFetch = new wxButton(this, ID_BUTTONFETCH, wxT("oldFetch"), wxPoint(142, 256), wxSize(59, 21), 0, wxDefaultValidator, wxT("buttonFetch"));
	buttonFetch->Show(false);

    buttonUberFetch = new wxButton(panelMain, ID_BUTTONUBERFETCH, wxT("Fetch"), wxPoint(308, 347), wxSize(49, 21), 0, wxDefaultValidator, wxT("buttonUberFetch"));
    buttonQuit = new wxButton(panelMain, ID_BUTTONQUIT, wxT("Quit"), wxPoint(358, 347), wxSize(40, 21), 0, wxDefaultValidator, wxT("buttonQuit"));


	timerFetch = new wxTimer();
	timerFetch->SetOwner(this, ID_TIMERFETCH);
	timerFetch->Start(10800000);

	timerFetchSingle = new wxTimer();
	timerFetchSingle->SetOwner(this, ID_TIMERFETCHSINGLE);

	SetTitle(wxT("RSS Filter Configuration"));
	SetIcon(wxNullIcon);
	SetSize(8,8,402,404);


	////GUI Items Creation End






	// make the html frame
	html         = new htmlFrame(this);
	fetching     = 0;

	// see to it that "User" dir exists
	if(!wxDirExists(wxT("User"))) wxMkdir(wxT("User"));

	// first thing is to load the master list (if there is one)
    master.Load(wxT("User/master.txt"));

    // load the internet resource list
    if(!wxFile::Exists(wxT("User/feeds.txt")))
    {
        Log(wxT("File 'User/feeds.txt' does not exist. Creating a simple example file..."));
        wxFile f;
        if(f.Open(wxT("User/feeds.txt"), wxFile::write))
        {
            f.Write(wxT("http://export.arxiv.org/rss/cond-mat|cond-mat\nhttp://feeds.aps.org/rss/recent/prl.xml|prl\n"));
            f.Close();
        }
    }
    feeds.Load(wxT("User/feeds.txt"));
    listURLs->Clear();
    for(int n=0; n<feeds.GetCount(); n++) listURLs->Append(feeds.list[n].title);

    // now set up the tray icon
    wxInitAllImageHandlers();
    some.LoadFile(wxT("Images/some.gif"), wxBITMAP_TYPE_GIF);
    none.LoadFile(wxT("Images/none.gif"), wxBITMAP_TYPE_GIF);
    updating.LoadFile(wxT("Images/updating.gif"), wxBITMAP_TYPE_GIF);
    shit.LoadFile(wxT("Images/error.gif"), wxBITMAP_TYPE_GIF);

    tray = new JaxTaskBarIcon(this);

    // load the gui stuff saved in the config file
    LoadConfiguration();

    // if we're supposed to limit the number of articles, do so
    if(checkBoxLimit->GetValue()) master.TrimOldArticles(spinLimit->GetValue());

    FilterAndShow();

    Log(wxT("Welcome to RSS Filter!"));

    // update on the internet
    Fetch();
}

void RSSFilterFrame::OnClose(wxCloseEvent& event)
{
    this->Hide();

    // first save all our settings
    SaveConfiguration();

    // now update the html filter page
    FilterAndShow();
}

void RSSFilterFrame::Quit()
{
    SaveConfiguration();
    master.Save(wxT("User/master.txt"));

    html->Hide();
    delete html;

    tray->RemoveIcon();
    delete tray;

    Destroy();
}

void RSSFilterFrame::buttonQuitClick(wxCommandEvent& event)
{
    Quit();
}

void RSSFilterFrame::SaveConfiguration()
{
    wxFile f;

    if(f.Open(wxT("User/config.txt"), wxFile::write))
    {
        s.sprintf(wxT("%s\n%s\n%s\n%i\n%i\n%i\n%i\n%i"),
              editFilter -> GetValue().GetData(),
              editReject -> GetValue().GetData(),
              editEmail  -> GetValue().GetData(),
              checkBoxTitle       -> GetValue(),
              checkBoxDescription -> GetValue(),
              checkBoxCreator     -> GetValue(),
              checkBoxLimit       -> GetValue(),
              spinLimit           -> GetValue()
              );

        f.Write(s.GetData());
        f.Close();
    }
}

void RSSFilterFrame::LoadConfiguration()
{
    wxTextFile f;

    if(wxFile::Exists(wxT("User/config.txt")) && f.Open(wxT("User/config.txt")))
    {
        editFilter -> SetValue(SafeGetLine(f,0));
        editReject -> SetValue(SafeGetLine(f,1));
        editEmail  -> SetValue(SafeGetLine(f,2));
        if(SafeGetLine(f,3).Strip() == wxT("1")) checkBoxTitle -> SetValue(true);
        else                                     checkBoxTitle -> SetValue(false);
        if(SafeGetLine(f,4).Strip() == wxT("1")) checkBoxDescription -> SetValue(true);
        else                                     checkBoxDescription -> SetValue(false);
        if(SafeGetLine(f,5).Strip() == wxT("1")) checkBoxCreator -> SetValue(true);
        else                                     checkBoxCreator -> SetValue(false);
        if(SafeGetLine(f,6).Strip() == wxT("1")) checkBoxLimit -> SetValue(true);
        else                                     checkBoxLimit -> SetValue(false);

        long limit;
        s = SafeGetLine(f,7).Strip();
        if(s.ToLong(&limit)) spinLimit->SetValue(limit);

        f.Close();
    }
}

void RSSFilterFrame::buttonFetchClick(wxCommandEvent& event)
{
    Fetch();
}

void RSSFilterFrame::Fetch()
{
    // pretend we pushed the uberfetch button
    wxCommandEvent e;
    e.SetId(ID_BUTTONUBERFETCH);
    e.SetEventType(wxEVT_COMMAND_BUTTON_CLICKED);
    AddPendingEvent(e);
}

int RSSFilterFrame::FilterString(wxString s, wxString filter_string)
{
    std::vector <wxArrayString> filter; // the vector is the split up |'s, and the sub-array is the +'s
    wxArrayString a, a2;
    int hits=0, good;

    if(filter_string==wxT("")) return 1;

    // make everything lowercase
    s             = s.Lower();
    filter_string = filter_string.Lower();

    // parse the filter string

    // split by the "or" ("|") symbol
    a = SplitString(filter_string.Strip(), wxT("|"));

    // loop over the array elements, splitting by the "and" ("+") symbol and
    // store it all in the filter vector
    for(size_t n=0; n<a.GetCount(); n++)
    {
        a2 = SplitString(a[n].Strip(), wxT("+"));
        filter.push_back(a2);
    }

    // loop over the or's
    for(int n=0; (unsigned int)n<filter.size(); n++)
    {
        // assume it's good until we find a missing element
        good=1;

        // loop over the and's
        for(int m=0; (unsigned int)m<filter[n].GetCount(); m++)
        {
            // they all must be found in s, or else it's a no-go
            if((int)s.find(filter[n][m]) == wxNOT_FOUND) good=0;
        }

        hits += good;
    }

    return hits;
}

void RSSFilterFrame::buttonShowClick(wxCommandEvent& event)
{
    FilterAndShow();
}

void RSSFilterFrame::FilterAndShow()
{
    // This function does a full sweep through the master article list
    // then stores the filtered ones and sets the html.

    int hits;

    // just to be sure it is always updated, trim the old articles
    if(checkBoxLimit->GetValue()) master.TrimOldArticles(spinLimit->GetValue());

    // now loop over all the guys in the master list and add the ones that pass
    // the filter to the filtered list.
    filtered.Clear();
    for(int n=0; n<master.GetCount(); n++)
    {
        // assemble the string we wish to search
        s=wxT("");
        if(checkBoxTitle->GetValue())       s = s+wxT("|")+master.list[n].title;
        if(checkBoxDescription->GetValue()) s = s+wxT("|")+master.list[n].description;
        if(checkBoxCreator->GetValue())     s = s+wxT("|")+master.list[n].creator;

        // now search for including strings.
        hits =  FilterString(s, editFilter->GetValue());

        // now search for more excluding strings
        if(editReject->GetValue() != wxT(""))
            hits = hits*(!FilterString(s, editReject->GetValue()));

        if(hits && (master.list[n].mode==wxT("enabled"))) // we got a goody that we've not removed manually
        {
            // add this to the filtered list.
            filtered.Append(master.list[n].link,        master.list[n].title,
                            master.list[n].description, master.list[n].creator,
                            master.list[n].mode);
        }
    }

    ShowFiltered();

}

void RSSFilterFrame::ShowFiltered()
{
    // This just loops over filtered and shows them on the html frame

    wxString email; //, citeulike;
    wxString source, color;
    int n;

    source = wxT("");
    for(n=0; n<filtered.GetCount(); n++)
    {
        // assemble the citeulike link
        //citeulike = "http://www.citeulike.org/posturl?url=" + filtered.list[n].link;

        // assemble the email link
        email = editEmail->GetValue().Strip() +
                wxT("?subject=") + EscapeURL(filtered.list[n].title) +
                wxT("&body=")    + EscapeURL(filtered.list[n].link);

        if(n % 2) color=wxT("#FAFAFF");
        else      color=wxT("#F4F4FF");

        // append this item to the html window source
        source = source +
                 wxT("<tr bgcolor='")+color+wxT("'><td valign='top'><a href='disable:") + filtered.list[n].link + wxT("'><img border=0 src='Images/x.gif'></a></td>") +
                 wxT("<td>") + filtered.list[n].title +
                 wxT(" <a href='") + filtered.list[n].link + wxT("'>[more]</a>") +
                 wxT(" <a href='mailto:")+email+wxT("'>[email]</a></td>\n");
                 // +
                 //" <a href='" + citeulike + "'>[citeulike]</a></td>\n";
    }

    s.sprintf(wxT("<b>%i Filtered Articles (%i Archived)</b> <a href='exit'>[quit]</a> <a href='configure'>[configure]</a> <a href='fetch'>[fetch]</a> <a href='recall'>[recall]</a>"), filtered.GetCount(), master.GetCount());

    if(undo.GetCount()) s=s+wxT(" <a href='undo'>[undo]</a>");

    source = s+wxT("<br><br><table cellspacing='0' cellpadding='5'>\n")+source+wxT("</table>");

    html->SetPage(source);

    Status();
}

void RSSFilterFrame::buttonRecallClick(wxCommandEvent& event)
{
    // loop through all the master list and enable all the articles
    for(size_t n=0; n<master.list.size(); n++)
        master.list[n].mode = wxT("enabled");

    FilterAndShow();

    undo.Clear();
}

void RSSFilterFrame::timerFetchTimer(wxTimerEvent& event)
{
    Fetch();
}

bool RSSFilterFrame::Status()
{
    // checks if there's an update in progress
    // updates the tray icon appropriately
    // returns true if we're allowed to write files

    if(fetching) return false;

    if(puked.GetCount()>0) // check for a fetch error
    {
        s.sprintf(wxT("%i feeds puked during update (%i new articles)"), puked.GetCount(), filtered.GetCount());
        tray->SetIcon(shit, s);
    }
    else if(filtered.GetCount())
    {
        s.sprintf(wxT("%i new articles"), filtered.GetCount());
        tray->SetIcon(some, s);
    }
    else tray->SetIcon(none, wxT("no new articles"));


    return true;
}


void RSSFilterFrame::buttonRemoveFeedClick(wxCommandEvent& event)
{
	// get the selection
    int item = listURLs->GetSelection();
	if (item != wxNOT_FOUND) feeds.RemoveItem(item);
	else Debug(wxT("Can't remove: No feed selected!"));

    // repopulate
	listURLs->Clear();
    for(int n=0; n<feeds.GetCount(); n++) listURLs->Append(feeds.list[n].title);
    feeds.Save(wxT("User/feeds.txt"));
}

void RSSFilterFrame::buttonNewFeedClick(wxCommandEvent& event)
{
	wxString name, link;

    link = wxGetTextFromUser(wxT("Enter the feed's URL:"), wxT("Feed URL"));
    if(link == wxT("")) return;

    name = wxGetTextFromUser(wxT("Enter a short name describing this feed:"), wxT("Feed Name"));
    if(name == wxT("")) return;

    feeds.Append(link, name, wxT(" "), wxT(" "), wxT(" "));

    // repopulate
	listURLs->Clear();
    for(int n=0; n<feeds.GetCount(); n++) listURLs->Append(feeds.list[n].title);
    feeds.Save(wxT("User/feeds.txt"));
}

void RSSFilterFrame::editFilterEnter(wxCommandEvent& event)
{
	FilterAndShow();
}

void RSSFilterFrame::editRejectEnter(wxCommandEvent& event)
{
	FilterAndShow();
}

void RSSFilterFrame::Log(wxString s)
{
    wxDateTime d = wxDateTime::Now();
    memoLog->AppendText(d.Format()+wxT(": ")+s+wxT("\n---------------------------------------------\n"));
}
void RSSFilterFrame::Debug(wxString s, int level)
{
    if(checkBoxDebug->GetValue() && level < 1 || checkBoxUberdebug->GetValue() && level >= 0)
    {
        wxDateTime d = wxDateTime::Now();
        memoLog->AppendText(d.Format()+wxT(": ")+s+wxT("\n---------------------------------------------\n"));
    }
}

void RSSFilterFrame::AddToPuked(Article feed)
{
    if(puked.Find(feed.link) <0) puked.Append(feed.link, feed.title);
    tray->SetIcon(shit, feed.title+wxT(" puked."));
}

void RSSFilterFrame::RecursivelyLoopOverNodes(wxXmlNode *start, wxString &feed_title)
{
    if(start == NULL) return;

    wxXmlNode *node;
    wxString link, title, description, creator;

    // first find how deep we must go to get an "<item>" tag
    Debug(wxT("Recursively looping over all the nodes, looking for <item>'s..."), 1);
    for(node = start; node != NULL; node=node->GetNext())
    {
        // if this is an item node, get the info and append
        if(node->GetName() == wxT("item"))
        {
            Debug(wxT("Found an <item> in '")+start->GetName()+wxT("'."), 1);

            // get all the different elements
            link        = GetNodeContent(node, wxT("link")).Strip();
            title       = GetNodeContent(node, wxT("title")).Strip();
            description = GetNodeContent(node, wxT("description")).Strip();
            creator     = GetNodeContent(node, wxT("dc:creator")).Strip();

            Debug(wxT("node '")+node->GetName()+wxT("':\n")+
                  wxT("title = ") + title + wxT("\n") +
                  wxT("description = ") + description + wxT("\n") +
                  wxT("link = ") + link + wxT("\n") +
                  wxT("creator = ") + creator + wxT("\n"), 1);

            // add the (prl) tag to the title
            title = wxT("(")+feed_title+wxT(") ")+title;

            // now if this link is not in the master file, add it
            if(master.Find(link) < 0)
            {
                master.Append(link, title, description, creator, wxT("enabled"));
                if(checkBoxLimit->GetValue()) master.TrimOldArticles(spinLimit->GetValue());
            }
        }
        else // otherwise we have to look at this node's children
        {
            Debug(wxT("Looking into node '")+node->GetName()+wxT("'..."), 1);
            RecursivelyLoopOverNodes(node->GetChildren(), feed_title);
        }
    }
    return;
}

void RSSFilterFrame::FetchSingleFeed(Article feed)
{
    wxString link, title, description, creator;
    wxInputStream *stream;
    wxXmlDocument xml;

    // update gui
    Debug(wxT("Updating ")+feed.title+wxT(" ..."));
    tray->SetIcon(updating, wxT("updating ")+feed.title);

    // make a new url object
    wxURL url(feed.link);

    // make sure the address is valid
    if (url.GetError() != wxURL_NOERR)
    {
        Log(feed.link+wxT("is a silly address. SILLY."));
        return;
    }

    // Pause for other user inputs before we try something this slow
    wxYield();

    // set the timeout to something reasonable and get the stream
    url.GetProtocol().SetTimeout(10);
    stream = url.GetInputStream();
    if (!stream)
    {
        Debug(wxT("Could not connect."));
        AddToPuked(feed);
        return;
    }

    // Pause again before we start downloading/parsing
    wxYield();

    // now hook up the stream to the xml document for parsing
    wxLogNull *StopFrickinComplaining = new wxLogNull();
    if(!xml.Load(*stream))
    {
        Log(wxT("Invalid XML file at ")+feed.link+wxT(". Try checking the address in your browser (and make sure it's not automatically redirecting you)."));
        return;
    }
    delete StopFrickinComplaining; StopFrickinComplaining=NULL;

    // now recursively loop over all the nodes, starting at the root
    RecursivelyLoopOverNodes(xml.GetRoot(), feed.title);

    // write the master file
    master.Save(wxT("User/master.txt"));

    delete stream;
}

void RSSFilterFrame::buttonUberFetchClick(wxCommandEvent& event)
{
    if(fetching) return; // don't let the user push it over and over.

    fetching = 1;

	// set some flags
	tray->SetIcon(updating, wxT("update process started"));

    // since we're updating all, clear the puked list
    puked.Clear();
    timerFetchSingle->Stop();

    // loop over all the feeds and fetch them
    for(int n=0; n<feeds.GetCount(); n++) FetchSingleFeed(feeds.list[n]);


    // see if any puked
    if(puked.GetCount() > 0)
    {
        s.sprintf(wxT("Finished. %i feeds failed. Retry in 30 seconds..."), puked.GetCount());
        timerFetchSingle->Stop();
        timerFetchSingle->Start(30000, true);
    }

    else
        s.sprintf(wxT("Update complete with no problems!"));
    Debug(s);

    fetching = 0;

    FilterAndShow(); // also updates status
}

wxString RSSFilterFrame::GetNodeContent(wxXmlNode *node, wxString name)
{
    wxXmlNode *child;

    // loop over all the children
    for(child = node->GetChildren(); child != NULL; child = child->GetNext())
    {
        // if this child is named "name", return the content
        if(child->GetName() == name) return child->GetNodeContent().Strip();
    }

    return wxT("(no ")+name+wxT(")");
}

void RSSFilterFrame::timerFetchSingleTimer(wxTimerEvent& event)
{
    if(fetching) return; // this should never happen anyway

    fetching = 1;

    s.sprintf(wxT("Retrying the %i feeds that failed..."), puked.GetCount());
	Debug(s);

    // this timer is a single shot update of the puked queue
	ArticleList temp;
	temp.Clear();

	// temporarily store the queue
    for(int n=0; n<puked.GetCount(); n++) temp.Append(puked.list[n].link, puked.list[n].title);

	// clear out the puked list
	puked.Clear();

	// loop over the temp queue
	for(int n=0; n<temp.GetCount(); n++) FetchSingleFeed(temp.list[n]);

	// see if any puked
    if(puked.GetCount() > 0)
    {
        s.sprintf(wxT("%i feeds still failed. Next try in 30 seconds..."), puked.GetCount());
        timerFetchSingle->Stop();
        timerFetchSingle->Start(30000, true);
    }
    else
        s.sprintf(wxT("Update complete with no problems!"));
    Debug(s);

    fetching = 0;

    FilterAndShow(); // also updates status
}


void RSSFilterFrame::checkBoxLimitClick(wxCommandEvent& event)
{
	if(checkBoxLimit->GetValue())
    {
        if(wxNO == wxMessageBox(wxT("Be careful! This option will automatically erase old articles from the (unfiltered) archives and cannot be undone, so you will never be able to search the archives again.\n\nEver.\n\nPlease double-check your limit value is sensible, and then NO COMPLAINING after you push 'yes'. Do you still want to enable this feature?"), wxT("Careful!"), wxYES_NO|wxCENTRE))
        {
            // they pushed no
            checkBoxLimit->SetValue(false);
        }
        else
        {
            // they pushed yes
            if(wxCopyFile(wxT("User/master.txt"), wxT("User/master_backup.txt"), true))
                wxMessageBox(wxT("I backed up the master archive 'User/master.txt' to 'User/master_backup.txt' anyway."), wxT("YOU delete it."));

            // update the article list
            FilterAndShow();
        }
    }
}


void RSSFilterFrame::checkBoxTitleClick(wxCommandEvent& event)
{
	FilterAndShow();
}

/*
 * panelMainUpdateUI
 */
void RSSFilterFrame::panelMainUpdateUI(wxUpdateUIEvent& event)
{
	// insert your code here
}

